﻿/*
 * Divisionsmatch - beregning af resultater
 * Copyright (C) 2013 Anders Klinting
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using LumenWorks.Framework.IO.Csv;
using System.Windows.Forms;

namespace Divisionsmatch
{
    public class Staevne
    {
        #region member variables
        private List<Klub> _klubber;
        private List<Match> _matcher;
        private List<Gruppe> _grupper;
        private SortedList<string, Loeber> _loebere;
        private Config _config;

        private string _printOutput = string.Empty;
        private string _productVersion = string.Empty;
        public int division { get; set; }
        #endregion

        #region constuctors
        public Staevne(string Productversion)
        {
            _productVersion = Productversion;
        }
        #endregion

        #region static methods

        public static IList<Match> GetMatcher(Staevne staevne, IList<string> klubNavne)
        {
            List<Klub> _tempklubber = new List<Klub>();
            foreach (string k in klubNavne)
            {
                _tempklubber.Add(new Klub(k));
            }
            return makeMatcher(staevne, _tempklubber);
        }

        private static IList<Match> makeMatcher(Staevne staevne, IList<Klub> klubber)
        {
            IList<Match> matcher = new List<Match>();
            for (int k = 0; k < klubber.Count; k++)
            {
                for (int l = k + 1; l < klubber.Count; l++)
                {
                    matcher.Add(new Match(staevne, klubber[k], klubber[l]));
                }
            }

            return matcher;
        }
        #endregion

        #region public properties
        public List<Klub> klubber
        {
            get
            {
                if (_klubber == null)
                {
                    _klubber = new List<Klub>();
                }
                return _klubber;
            }
        }

        public List<Match> matcher
        {
            get
            {
                if (_matcher == null)
                {
                    _matcher = new List<Match>();
                }
                return _matcher;
            }
        }

        public List<Gruppe> grupper
        {
            get
            {
                if (_grupper == null)
                {
                    _grupper = new List<Gruppe>();
                }
                return _grupper;
            }
        }

        public SortedList<string, Loeber> loebere
        {
            get
            {
                if (_loebere == null)
                {
                    _loebere = new SortedList<string, Loeber>();
                }
                return _loebere;
            }
        }

        public Config config
        {
            get
            {
                return _config;
            }
            set
            {
                _config = value;
            }
        }

        #endregion

        #region public methods

        public virtual void beregnpoint(Config config, string filnavn)
        {
            _load(config);
            beregnpoint(filnavn);
        }

        public virtual void beregnpoint(string filnavn)
        {
            // reset output
            _printOutput = string.Empty;

            // xml eller csv?
            string version = string.Empty;
            XmlDocument xmlDoc = new XmlDocument();
            bool isCSV = false;
            try
            {
                string allText = File.ReadAllText(filnavn);
                if (allText.Contains("<?xml version="))
                {
                    xmlDoc.XmlResolver = null;
                    xmlDoc.Load(filnavn);

                    // validate
                    XmlNodeList rootNodes = xmlDoc.GetElementsByTagName("ResultList");
                    if (rootNodes == null || rootNodes.Count != 1)
                    {
                        throw new Exception("Dette er vist ikke en XML ResultList");
                    }

                    // find ud af hvilken version vi har med at gøre
                    XmlAttribute iofVersion = rootNodes[0].Attributes["iofVersion"];
                    if (iofVersion == null)
                    {
                        XmlNodeList iofNodes = xmlDoc.GetElementsByTagName("IOFVersion");
                        if (iofNodes != null && iofNodes.Count == 1)
                        {
                            iofVersion = iofNodes[0].Attributes["Version"];
                        }
                    }
                    if (iofVersion != null)
                    {
                        version = iofVersion.Value;
                    }
                }
                else
                {
                    // vi antager det er en csv-fil
                    isCSV = true;
                }
            }
            catch (Exception e)
            {
                throw new Exception("Fejl ved load af XML: " + e.Message);
            }

            IList<Loeber> alleloebere = null;
            if (isCSV)
            {
                alleloebere = _readcsv(filnavn);
            }
            else
            {
                // load fra XML
                if (version == "3.0")
                {
                    alleloebere = _readXML3(filnavn);
                }
                else
                {
                    alleloebere = _readXML2(filnavn);
                }
            }

            loebere.Clear();
            foreach (Gruppe g in grupper)
            {
                g.loebere.Clear();
            }

            foreach (Loeber l in alleloebere)
            {
                // hvis løberen er fra en klub i divisionsmatchen
                bool bKlub = klubber.Exists(k => l.klub != null && k.Equals(l.klub));
                // placer løberen i løbet og i en gruppe
                Gruppe g = grupper.FirstOrDefault(grp => grp.harløbsklasse(l.løbsklassenavn));
                if (g != null)
                {
                    l.inkl = bKlub; // er løberen i matchen
                    // tilføj løber til løebet
                    // 18 ~ "kort", 15 ~ klub
                    this.loebere.Add(l.NumStatus + " - " + l.tid + " - " + this.loebere.Count.ToString().PadLeft(5), l);

                    // og registrer løberen til gruppen
                    g.loebere.Add(l.NumStatus + " - " + l.tid + " - " + this.loebere.Count.ToString().PadLeft(5), l);
                }
            }

            // for hver gruppe - fordel point
            foreach (Gruppe g in grupper)
            {
                foreach (Match m in matcher)
                {
                    g.fordelPoint(m);
                }
            }
        }

        public string printstilling(bool html)
        {
            StringBuilder output = new StringBuilder();

            ////klub                                     :     score      point
            ////Allerød OK                               :   325 -   283      4
            ////OK Midtvest                              :   190 -   378      0
            ////OK Roskilde                              :   307 -   296      2
            ////OK Øst Birkerød                          :   369 -   234      6
            SortedList<string, string> stilling = new SortedList<string, string>();
            if (!html) output.AppendLine("Stilling");
            if (!html) output.AppendLine("--------");
            if (!html) output.AppendLine("".PadRight(40) + "   " + "    score      point");
            if (html) output.AppendLine("<tr><td>&nbsp;</td><td colspan=3>score</td><td>point</td></tr>");
            int klubnr = 0;
            foreach (Klub k in klubber)
            {
                klubnr++;
                int p = 0;
                double score1 = 0.0;
                double score2 = 0.0;
                string ude = string.Empty;
                foreach (Match m in matcher)
                {
                    if (m.harKlub(k))
                    {
                        double mp1 = (m.klub1 == k) ? m.score1(): m.score2();
                        double mp2 = (m.klub1 == k) ? m.score2(): m.score1();

                        // summer scores - 1 for egen, 2 for andres
                        score1 += mp1;
                        score2 += mp2;

                        // tildeler point 0-1-2
                        p += (m.klub1 == k) ? m.matchpoint1(loebere) : m.matchpoint2(loebere);                
                    }
                }

                ude = k.udeblevet ? " (udeblevet)" : string.Empty;

                double delta = 100000.0 + score1 - score2;

                // man skal sorteres på point faldende, delta faldende, score1 faldende
                // konverter all til integers og print
                string key = p.ToString("00000").PadLeft(5) + delta.ToString("000000000000000") + score1.ToString("000000000000000") + klubnr.ToString("000");
                if (html)
                {
                    stilling.Add(key, "<tr><td>" + k.navn.PadRight(40) + "</td><td>" + score1.ToString("##0.#").PadLeft(5) + "</td><td>-</td><td>" + score2.ToString("##0.#").PadLeft(5) + "</td><td align=right>" + p.ToString("##0").PadLeft(5) + "</td><td align=right>&nbsp;" + ude + "</td></tr>");
                }
                else
                {
                    stilling.Add(key, k.navn.PadRight(40) + " : " + score1.ToString("##0.#").PadLeft(5) + " - " + score2.ToString("##0.#").PadLeft(5) + "  " + p.ToString("##0").PadLeft(5)+ude);
                }

            }
            foreach (var s in stilling.OrderByDescending(item => item.Key))
            {
                output.AppendLine(s.Value);
            }
            output.AppendLine();

            return output.ToString();
        }

        public string printmatcher()
        {
            if (_printOutput != string.Empty)
            {
                return _printOutput;
            }
            else
            {
                StringBuilder output = new StringBuilder();

                int n = 1;
                foreach (Match m in matcher)
                {
                    output.Append((n.ToString() + " : " + m.klub1.navn + " - " + m.klub2.navn).PadRight(50) + " : ");

                    double p1 = m.score1();
                    double p2 = m.score2();

                    output.AppendLine(p1.ToString("##0.#").PadLeft(5) + " - " + p2.ToString("##0.#").PadLeft(5));

                    n++;
                }

                output.AppendLine();

                // first calculate the width of each column
                List<int> p1widths = new List<int>();
                List<int> p2widths = new List<int>();
                int mm = 0;
                n = 0;
                foreach (Gruppe g in grupper)
                {
                    mm = 0;
                    foreach (Match m in matcher)
                    {
                        // double p1 = g.loebere.Where(item => item.Value.klub == m.klub1).Sum(item => item.Value.GetSumPoint(m));
                        // double p2 = g.loebere.Where(item => item.Value.klub == m.klub2).Sum(item => item.Value.GetSumPoint(m));

                        double p1 = m.loebspoint1(g.loebere);
                        double p2 = m.loebspoint2(g.loebere);

                        int o1 = p1.ToString("##0.#").Length;
                        int o2 = p2.ToString("##0.#").Length;

                        if (n == 0)
                        {
                            p1widths.Add(o1);
                            p2widths.Add(o2);
                        }
                        else
                        {
                            p1widths[mm] = p1widths[mm] < o1 ? o1 : p1widths[mm];
                            p2widths[mm] = p2widths[mm] < o2 ? o2 : p2widths[mm];
                        }
                        mm++;
                    }
                    n++;
                }

                // lav stilling
                output.Append(printstilling(false));

                // make match numbers
                int w = 0;
                foreach (Gruppe g in grupper)
                {
                    int wx = (g.navn.PadRight(3) + "(" + string.Join(", ", g.klasser.Select(k => k.GruppeKlasse).ToArray()) + ") ").Length;
                    if (wx > w) w = wx;
                }

                output.Append("".PadRight(w, ' '));
                n = 1;
                mm = 0;
                foreach (Match m in matcher)
                {
                    int w1 = p1widths[mm];
                    int w2 = p2widths[mm];
                    output.Append("m".PadLeft(w1) + n.ToString().PadRight(w2) + "  ");
                    mm++;
                    n++;
                }
                output.AppendLine();

                // print underline
                output.Append("".PadRight(w, ' '));
                mm = 0;
                foreach (Match m in matcher)
                {
                    int w1 = p1widths[mm];
                    int w2 = p2widths[mm];
                    output.Append(" ".PadLeft(w1 + w2 + 1 + 1, '-'));
                    mm++;
                }
                output.AppendLine();

                // print data
                foreach (Gruppe g in grupper)
                {
                    mm = 0;
                    output.Append((g.navn.PadRight(3) + "(" + string.Join(", ", g.klasser.Select(k => k.GruppeKlasse).ToArray()) + ") ").PadRight(w, ' '));
                    foreach (Match m in matcher)
                    {
                        int w1 = p1widths[mm];
                        int w2 = p2widths[mm];

                        //double p1 = g.loebere.Where(item => item.Value.klub == m.klub1).Sum(item => item.Value.GetSumPoint(m));
                        //double p2 = g.loebere.Where(item => item.Value.klub == m.klub2).Sum(item => item.Value.GetSumPoint(m));
                        double p1 = m.loebspoint1(g.loebere);
                        double p2 = m.loebspoint2(g.loebere);

                        output.Append(p1.ToString("##0.#").PadLeft(w1) + "-" + p2.ToString("##0.#").PadRight(w2) + " ");
                        mm++;
                    }
                    output.AppendLine();
                }

                return output.ToString();
            }
        }

        public List<string> printLister(Config config)
        {
            List<string> output = new List<string>();

            if (!config.PrintBaner)
            {
                foreach (Gruppe g in grupper)
                {

                    var lg = g.loebere.Where(l => l.Value.inkl == true || config.PrintAlle);
                    if (lg.Count() > 0 || config.PrintAlleGrupper)
                    {
                        output.Add(g.printReport(matcher) + txtTable(g.loebere.Where(l => l.Value.inkl == true || config.PrintAlle).Select(ll => ll.Value).ToList()));
                    }
                }
            }
            else
            {
                foreach (Bane b in config.baner.OrderBy(bb => bb.Navn))
                {
                    var kl = config.classes.Where(k => k.Bane != null && k.Bane.Navn.Equals(b.Navn)).Select(kk => kk.Navn);
                    // find løbere på samme bane - og vælg dem i matchen, eller alle
                    var lll = loebere.Where(l => kl.Contains(l.Value.løbsklassenavn) && (l.Value.inkl == true || config.PrintAlle)).Select(ll => ll.Value).ToList();
                    if (lll.Count > 0 || config.PrintAlleGrupper)
                    {
                        output.Add(b.printReport() + txtTable(lll));
                    }
                }
            }
            return output;
        }

        public List<string> lavHTMLafsnit(Config config)
        {
            List<string> htmlSections = new List<string>();

            htmlSections.Add(_lavHTMLStilling(config));

            if (!config.PrintBaner)
            {
                // gruppe detalje resultater
                foreach (Gruppe g in grupper)
                {
                    var lll = g.loebere.Where(l => l.Value.inkl == true || config.PrintAlle);
                    if (lll.Count() > 0 || config.PrintAlleGrupper)
                    {
                        StringBuilder html = new StringBuilder();
                        html.AppendLine(g.lavHTML(matcher));
                        html.AppendLine(htmlTable(g.loebere.Where(l => l.Value.inkl == true || config.PrintAlle).Select(ll => ll.Value).ToList()));

                        htmlSections.Add(html.ToString());
                    }
                }
            }
            else
            {
                foreach (Bane b in config.baner.OrderBy(bb => bb.Navn))
                {
                    StringBuilder html = new StringBuilder();

                    var kl = config.classes.Where(k => k.Bane != null && k.Bane.Navn.Equals(b.Navn)).Select(kk => kk.Navn);
                    // find løbere på samme bane - og vælg dem i matchen, eller alle
                    var lll = loebere.Where(l => kl.Contains(l.Value.løbsklassenavn) && (l.Value.inkl == true || config.PrintAlle)).Select(ll => ll.Value).ToList();
                    if (lll.Count > 0 || config.PrintAlleGrupper)
                    {
                        html.AppendLine(b.lavHTML());
                        html.AppendLine(htmlTable(lll));

                        htmlSections.Add(html.ToString());
                    }
                }
            }

            return htmlSections;
        }

        public string lavHTML(List<string> sections)
        {
            StringBuilder html = new StringBuilder();
            
            html.AppendLine("<html><head><title>Divisionsmatch v " + _productVersion + " - (" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ")</title>");
            html.AppendLine(getHTMLStyle());
            html.AppendLine("</head><body>");

            bool bFirst = true;
            foreach (string s in sections)
            {
                if (!bFirst)
                {
                    if (_config != null && _config.SideSkift)
                    {
                        html.AppendLine("<div class='page-break'></div>");
                    }
                }
                else
                {
                    bFirst = false;
                }

                html.AppendLine(s);
            }

            html.AppendLine("</body></html>");

            return html.ToString();
        }

        #endregion

        #region private methods

        private string _lavHTMLStilling(Config config)
        {
            StringBuilder html = new StringBuilder();

            html.AppendLine("<h3>Matcher</h3>");
            html.AppendLine("<table cellpadding=2 cellspacing=2>");
            html.AppendLine("<tbody>");
            int n = 1;
            foreach (Match m in matcher)
            {
                html.Append("<tr><td>" + n.ToString() + "</td><td> : </td><td>" + m.klub1.navn + "</td><td> - </td><td>" + m.klub2.navn + "</td><td> : </td><td align=right>");

                //double p1 = loebere.Where(item => item.Value.klub == m.klub1).Sum(item => item.Value.GetSumPoint(m));
                //double p2 = loebere.Where(item => item.Value.klub == m.klub2).Sum(item => item.Value.GetSumPoint(m));

                double p1 = m.score1();
                double p2 = m.score2();
                
                html.AppendLine(p1.ToString("##0.#") + "</td><td> - </td><td align=right>" + p2.ToString("##0.#") + "</td></tr>");

                n++;
            }
            html.AppendLine("</tbody></table>");

            html.AppendLine("<h3>Stilling</h3>");
            html.AppendLine("<table cellpadding=2 cellspacing=2>");
            html.AppendLine("<tbody>");

            html.AppendLine(printstilling(true));
            html.AppendLine("</tbody></table>");

            // matcherneper gruppe
            // make match numbers
            html.AppendLine("<p><table cellpadding=2 cellspacing=2>");
            html.AppendLine("<thead>");
            html.Append("<tr><th class='myBorder' >&nbsp;</th>");
            n = 1;
            foreach (Match m in matcher)
            {
                html.Append("<th class='myBorder' >m" + n.ToString() + "</th>");
                n = n + 1;
            }
            html.AppendLine("</tr>");
            html.AppendLine("</thead><tbody>");

            // print data
            foreach (Gruppe g in grupper)
            {
                html.Append("<tr><td class='myBorder'>" + g.navn);
                html.Append(" (" + string.Join(", ", g.klasser.Select(k => k.GruppeKlasse).ToArray()) + ")");
                html.Append("</td>");

                foreach (Match m in matcher)
                {
                    //double p1 = g.loebere.Where(item => item.Value.klub == m.klub1).Sum(item => item.Value.GetSumPoint(m));
                    //double p2 = g.loebere.Where(item => item.Value.klub == m.klub2).Sum(item => item.Value.GetSumPoint(m));
                    double p1 = m.loebspoint1(g.loebere);
                    double p2 = m.loebspoint2(g.loebere);
                    ////if (m.harUdeblevne)
                    ////{
                    ////    // halver point antallet
                    ////    p1 /= 2;
                    ////    p2 /= 2;
                    ////}

                    html.Append("<td class='myBorder'>" + p1.ToString("##0.#") + "-" + p2.ToString("##0.#") + "</td>");
                }
                html.AppendLine("</tr>");
            }
            html.AppendLine("</tbody></table>");

            return html.ToString();
        }

        private string getHTMLStyle()
        {
            string fontName = _config != null ? _config.font.FontValue.Name : "Courier New";
            string fontSize = _config != null ? _config.font.FontValue.SizeInPoints.ToString() : "10";
            bool bold = _config != null ? _config.font.FontValue.Bold : false;
            bool italic = _config != null ? _config.font.FontValue.Italic: false;
            bool strike = _config != null ? _config.font.FontValue.Strikeout : false;
            bool underline = _config != null ? _config.font.FontValue.Underline : false;

            string style = string.Empty;

            style += @"<style type='text/css' media='print,screen' >";
            style += @"table, h3, body {font-family:" + fontName + @";font-size:" + fontSize + @"pt";
            if (bold)
            {
                style += @";font-weight:bold";
            }
            if (italic)
            {
                style += @";font-style:italic";
            }
            if (underline || strike)
            {
                style += @";text-decoration:" + (underline ? "underline" : "normal") + " normal " + (strike ? "line-through" : "normal") + " normal";
            }
            style += @"}";

            style += @"h3 {font-size: larger;} ";
            style += @"tr {page-break-inside: avoid;} ";
            style += @".myBorder {border-bottom: solid lightgrey 1px;border-left: solid lightgrey 1px;}   ";
            style += @".page-break {page-break-before:always;}";
            style += @"thead { display:table-header-group;}";
            style += @"tbody { display:table-row-group; } ";
            style += @"</style>";

            //// style += "<script>setTimeout(\"document.body.style.zoom=1.5\", 500)</script>";

            return style;
        }

        private void _load(Config config)
        {
            // clear det hele
            _klubber = null;
            _grupper = null;
            _matcher = null;

            _config = config;

            division = _config.selectedDivision;

            foreach (string k in _config.selectedClubs)
            {
                Klub klub = new Klub(k);
                if (config.udeblevneKlubber.Contains(k))
                {
                    klub.udeblevet = true;
                }
                klubber.Add(klub);
            }

            foreach (GruppeOgKlasse gk in _config.gruppeOgKlasse) //.Where(item => item.LøbsKlasse.Trim() != "-" && item.LøbsKlasse.Trim() != string.Empty))
            {
                if (gk.LøbsKlasse != null && gk.LøbsKlasse.Trim() != "-" && gk.LøbsKlasse.Trim() != string.Empty)
                {
                    Gruppe g = grupper.Find(item => item.navn == gk.Gruppe);
                    if (g == null)
                    {
                        // lav gruppen
                        g = new Gruppe(gk.Gruppe, division);
                        grupper.Add(g);
                    }

                    // tilføj klasse definitionen
                    if (!g.klasser.Exists(k => k.LøbsKlasse != null && k.LøbsKlasse.Navn.Equals(gk.LøbsKlasse)))
                    {
                        Klasse kk = _config.classes.Find(c => c.Navn == gk.LøbsKlasse);
                        g.klasser.Add(new klasseconfig(gk.Klasse, kk, gk.Ungdom));
                    }
                }
            }

            matcher.AddRange(makeMatcher(this, klubber));
        }

        private IList<Loeber> _readcsv(string filnavn)
        {
            string msg = string.Empty;

            IList<Loeber> loebere = new List<Loeber>();

            // find løbere og tilføj dem til gruppen
            // open the file "data.csv" which is a CSV file with headers
            using (CsvReader csv = new CsvReader(new StreamReader(filnavn, ASCIIEncoding.Default), false, ';'))
            {
                if (csv.FieldCount < 43)
                {
                    msg += "det er vist ikke en csv-fil...\n";
                }
                else
                {
                    // csv.MissingFieldAction = MissingFieldAction.ReplaceByNull;

                    csv.MissingFieldAction = MissingFieldAction.ReplaceByEmpty;
                    csv.DefaultParseErrorAction = ParseErrorAction.AdvanceToNextLine;

                    // skip header
                    csv.ReadNextRecord();
                    while (csv.ReadNextRecord())
                    {
                        try
                        {
                            // løberen må have en klasse som bruges i matchen
                            var gk = _config.gruppeOgKlasse.FirstOrDefault(k => k.LøbsKlasse == csv[18]);
                            if (gk != null)
                            {
                                Loeber l = new Loeber();
                                l.stnr = csv[0]; //"startnr"];
                                l.fornavn = csv[4]; //"fornavn"];
                                l.efternavn = csv[3]; // "efternavn"];
                                l.klub = klubber.Find(item => item.navn.Equals(csv[15]));//"klub"
                                if (l.klub == null)
                                {
                                    // add a temporay klub for runners outside the match
                                    l.klub = new Klub(csv[15]);
                                }
                                l.løbsklassenavn = csv[18];
                                l.gruppeklasse = gk.Klasse;
                                l.placering = csv[43]; //"pl"];
                                l.tid = _lavtid(csv[11]); //"tid"];
                                l.status = csv[12]; //"status"];

                                loebere.Add(l);
                            }
                        }
                        catch
                        {
                            msg += "linje: " + csv.CurrentRecordIndex + "\n";
                        }
                    }
                }
            }

            if (msg != string.Empty)
            {
                throw new Exception("Problemer med indlæsning af csv-data: \n" + msg);
            }

            return loebere;
        }

        private IList<Loeber> _readXML2(string filnavn)
        {
            string msg = string.Empty;

            IList<Loeber> loebere = new List<Loeber>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.XmlResolver = null;
            xmlDoc.Load(filnavn);

            XmlNodeList classNodes = xmlDoc.GetElementsByTagName("ClassResult");
            foreach (XmlElement classNode in classNodes)
            {
                // class detail
                string classtext = string.Empty;
                XmlNodeList classes = classNode.GetElementsByTagName("ClassShortName");
                if (classes.Count > 0)
                {
                    classtext = classNode.GetElementsByTagName("ClassShortName")[0].InnerText;
                }

                // deltagere
                foreach (XmlElement personResult in classNode.GetElementsByTagName("PersonResult"))
                {
                    // person details
                    XmlElement person = personResult.GetElementsByTagName("Person")[0] as XmlElement;
                    XmlElement personName = person.GetElementsByTagName("PersonName")[0] as XmlElement;

                    string efternavn = personName.GetElementsByTagName("Family")[0].InnerText;
                    string fornavn = personName.GetElementsByTagName("Given")[0].InnerText;

                    // club details
                    XmlElement club = personResult.GetElementsByTagName("Club")[0] as XmlElement;
                    string klubnavn = club.GetElementsByTagName("ShortName")[0].InnerText;

                    // time details
                    XmlElement result = personResult.GetElementsByTagName("Result")[0] as XmlElement;
                    XmlNodeList resultTimes = result.GetElementsByTagName("Time");
                    string tid = string.Empty;
                    if (resultTimes.Count > 0)
                    {
                        tid = resultTimes[0].InnerText;
                    }

                    string placering = string.Empty;
                    XmlNodeList resultPositions = result.GetElementsByTagName("ResultPosition");
                    if (resultPositions.Count > 0)
                    {
                        placering = resultPositions[0].InnerText;
                    }
                    string status = result.GetElementsByTagName("CompetitorStatus")[0].Attributes["value"].Value;

                    try
                    {
                        // løberen må have en klasse som bruges i matchen
                        var gk = _config.gruppeOgKlasse.FirstOrDefault(k => k.LøbsKlasse == classtext);
                        if (gk != null)
                        {
                            Loeber l = new Loeber();
                            l.stnr = "0"; //""];
                            l.fornavn = fornavn; //"fornavn"];
                            l.efternavn = efternavn; // "efternavn"];
                            l.klub = klubber.Find(item => item.navn.Equals(klubnavn));//"klub"
                            if (l.klub == null)
                            {
                                // add a temporay klub for runners outside the match
                                l.klub = new Klub(klubnavn);
                            }
                            l.løbsklassenavn = classtext;
                            l.gruppeklasse = gk.Klasse;
                            l.placering = placering; //"pl"];
                            l.tid = _lavtid(tid); //"tid"];
                            l.status = status; //"status"];

                            loebere.Add(l);
                        }
                    }
                    catch
                    {
                        msg += "løber: " + fornavn + " " + efternavn + " kunne ikke tilføjes\n";
                    }
                }
            }

            if (msg != string.Empty)
            {
                throw new Exception("Problemer med indlæsning af xml-data: \n" + msg);
            }
            return loebere;
        }

        private IList<Loeber> _readXML3(string filnavn)
        {
            string msg = string.Empty;
            IList<Loeber> loebere = new List<Loeber>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.XmlResolver = null;
            xmlDoc.Load(filnavn);

            XmlNodeList classNodes = xmlDoc.GetElementsByTagName("ClassResult");
            foreach (XmlElement classNode in classNodes)
            {
                // class detail
                string classtext = (classNode.GetElementsByTagName("Class")[0] as XmlElement).GetElementsByTagName("Name")[0].InnerText;

                foreach (XmlElement personResult in classNode.GetElementsByTagName("PersonResult"))
                {
                    // person details
                    XmlElement person = personResult.GetElementsByTagName("Person")[0] as XmlElement;
                    XmlElement personName = person.GetElementsByTagName("Name")[0] as XmlElement;

                    string efternavn = personName.GetElementsByTagName("Family")[0].InnerText;
                    string fornavn = personName.GetElementsByTagName("Given")[0].InnerText;

                    // club details
                    XmlElement club = personResult.GetElementsByTagName("Organisation")[0] as XmlElement;
                    string klubnavn = club.GetElementsByTagName("Name")[0].InnerText;

                    // time details
                    XmlElement result = personResult.GetElementsByTagName("Result")[0] as XmlElement;
                    
                    XmlNodeList resultTimes = result.GetElementsByTagName("Time");
                    string tid = string.Empty;
                    if (resultTimes.Count>0)
                    {
                        tid = resultTimes[0].InnerText;
                    }
                    
                    string placering = string.Empty;
                    XmlNodeList resultPositions = result.GetElementsByTagName("Position");
                    if (resultPositions.Count > 0)
                    {
                        placering = resultPositions[0].InnerText;
                    }
                    
                    string status = string.Empty;
                    XmlNodeList resultStatus = result.GetElementsByTagName("Status");
                    if (resultStatus.Count > 0)
                    {
                        status = resultStatus[0].InnerText;
                    }

                    try
                    {
                        // løberen må have en klasse som bruges i matchen
                        var gk = _config.gruppeOgKlasse.FirstOrDefault(k => k.LøbsKlasse == classtext);
                        if (gk != null)
                        {
                            Loeber l = new Loeber();
                            l.stnr = "0"; //""];
                            l.fornavn = fornavn; //"fornavn"];
                            l.efternavn = efternavn; // "efternavn"];
                            l.klub = klubber.Find(item => item.navn.Equals(klubnavn));//"klub"
                            if (l.klub == null)
                            {
                                // add a temporay klub for runners outside the match
                                l.klub = new Klub(klubnavn);
                            }
                            l.løbsklassenavn = classtext;
                            l.gruppeklasse = gk.Klasse;
                            l.placering = placering; //"pl"];
                            l.tid = _lavtid(tid); //"tid"];
                            l.status = status; //"status"];

                            loebere.Add(l);
                        }
                    }
                    catch
                    {
                        msg += "løber: " + fornavn + " " + efternavn + "\n";
                    }
                }
            }

            if (msg != string.Empty)
            {
                throw new Exception("Problemer med indlæsning af xml-data: \n" + msg);
            }

            return loebere;
        }

        private string _lavtid(string t)
        {
            string nytid = t;

            string[] timeparts = t.Split(':');
            if (timeparts.Length == 1)
            {
                if (!t.Trim().Equals(string.Empty))
                {
                    // antag at det er antal sekunder
                    int secs = Convert.ToInt32(t);
                    TimeSpan ts = new TimeSpan(secs * 10000000L);
                    nytid = string.Format("{0:c}", ts);
                }
            }
            else if (timeparts.Length == 2)
            {
                // antag at det er minutter og sekunder
                nytid = "00:" + t.PadLeft(5, '0');
            }
            else if (timeparts.Length == 3)
            {
                // foranstil med nuller
                nytid = t.PadLeft(8, '0');
            }

            return nytid;
        }

        private string htmlTable(IList<Loeber> loebere)
        {
            StringBuilder output = new StringBuilder();

            output.AppendLine("<table cellpadding=2 cellspacing=2>");
            output.Append("<thead><tr><th class='myBorder'>pl</th><th class='myBorder'>navn</th><th class='myBorder'>klub</th><th class='myBorder'>klasse</th><th class='myBorder'>tid</th>");
            for (int i = 1; i <= matcher.Count; i++)
            {
                output.Append("<th class='myBorder'>m" + i.ToString() + "</th>");
            }
            output.AppendLine("</tr></thead>");
            output.AppendLine("<tbody>");

            string oldTid = "";
            int pl = 0;
            int cnt = 0;
            foreach (var l in loebere)
            {
                cnt++;
                // Loeber l = kv.Value;
                string line = "<tr>";
                if (l.IsStatusOK)
                {
                    if (oldTid != l.tid)
                    {
                        pl = cnt;
                    }
                    oldTid = l.tid;
                }
                line += "<td class='myBorder' align=right>" + (l.IsStatusOK ? pl.ToString() : "&nbsp;") + "</td>";
                line += "<td class='myBorder' nowrap>" + l.fornavn + " " + l.efternavn + "</td>";
                line += "<td class='myBorder' nowrap>" + l.klub.navn + "</td>";
                line += "<td class='myBorder'>" + l.løbsklassenavn + "</td>";
                line += "<td class='myBorder'>" + (l.IsStatusOK ? l.tid : l.PrintStatus) + "</td>";

                foreach (Match m in matcher)
                {
                    string up = l.GetUPoint(m) > 0 ? "*" : "&nbsp";
                    double p = l.GetSumPoint(m);
                    line += "<td class='myBorder' align=right>" + (p > 0 ? p.ToString("0.#") : "&nbsp;") + up + "</td>";
                }

                line += "</tr>";

                output.AppendLine(line);
            }

            output.AppendLine("</tbody>");
            output.AppendLine("</table>");

            return output.ToString();
        }

        private string txtTable(IList<Loeber> loebere)
        {
            StringBuilder output = new StringBuilder();

            output.Append("pl navn                    klub            klasse    tid  ");
            for (int i = 1; i <= matcher.Count; i++)
            {
                output.Append(("m" + i.ToString()).PadLeft(5));
            }
            output.AppendLine();

            string oldTid = "";
            int pl = 0;
            int cnt = 0;
            foreach (var l in loebere) // print onlu included runners
            {
                cnt++;
                //Loeber l = kv.Value;
                string line = string.Empty;
                if (l.IsStatusOK)
                {
                    if (oldTid != l.tid)
                    {
                        pl = cnt;
                    }
                    oldTid = l.tid;
                }
                line += (l.IsStatusOK ? pl.ToString().PadRight(3) : "   ");
                line += (l.fornavn + " " + l.efternavn).PadRight(24).Substring(0, 24);
                line += l.klub.navn.PadRight(15).Substring(0, 15);
                line += " ";
                line += l.løbsklassenavn.PadRight(7).Substring(0, 7);
                line += (l.IsStatusOK ? l.tid : l.PrintStatus).PadLeft(8).Substring(0, 8);


                string up = string.Empty;
                foreach (Match m in matcher)
                {
                    double p = l.GetSumPoint(m);
                    if (up != string.Empty)
                    {
                        line += (p > 0) ? p.ToString("0.#").PadLeft(4) : p.ToString("#.#").PadLeft(4);
                    }
                    else
                    {
                        line += (p > 0) ? p.ToString("0.#").PadLeft(5) : p.ToString("#.#").PadLeft(5);
                    }
                    up = l.GetUPoint(m) > 0 ? "*" : string.Empty;
                    line += up;
                }

                output.AppendLine(line);
            }

            output.AppendLine();

            return output.ToString();
        }
        #endregion

    }
}
